Skip to content

[Discounts] Described Discounts API #2783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 16, 2025
Merged

Conversation

mnocon
Copy link
Contributor

@mnocon mnocon commented Jun 10, 2025

This PR describes how you can use the Discounts PHP API and the core concepts needed to work with it.

Previews:

@mnocon mnocon changed the base branch from discounts to discounts-rest June 10, 2025 22:11
Copy link

@mnocon mnocon marked this pull request as ready for review June 11, 2025 06:41
@mnocon mnocon requested review from Steveb-p and konradoboza June 11, 2025 06:41
new IsInRegions(['germany', 'france']),
new IsProductInArray(['product-1', 'product-2']),
new IsInCurrency('EUR'),
new IsValidDiscountCode($discountCode->getCode(), $discountCode->getUsedLimit()),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I'm not sure about the $discountCode->getUsedLimit() part - which value should be passed here, the current usage or the max usage?

If I understand correctly, the condition calls the resolver which takes a single argument - so the second argument to IsValidDiscountCode is actually not used?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @Steveb-p can clarify, but to me it's the max usage.

$discountCode = $this->discountCodeService->createDiscountCode($discountCodeCreateStruct);

$discountCreateStruct = new DiscountCreateStruct();
$discountCreateStruct->setIdentifier('discount_identifier')
Copy link
Contributor Author

@mnocon mnocon Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've chosen the fluent API usage over the constructor, as this is self-documenting for the code sample - I'd just use the constructor in "standard" code.

I hope it will be clear for the readers that they can just use the constructor.

## Search

You can search for Discounts using the [`DiscountServiceInterface::findDiscounts()](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html#method_findDiscounts) method.
To learn more about the available search options, see Discounts' Search Criteria and Sort Clauses.

This comment was marked as outdated.

Copy link
Contributor

@konradoboza konradoboza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very solid overall 💪 A few remarks from my end:

new IsInRegions(['germany', 'france']),
new IsProductInArray(['product-1', 'product-2']),
new IsInCurrency('EUR'),
new IsValidDiscountCode($discountCode->getCode(), $discountCode->getUsedLimit()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @Steveb-p can clarify, but to me it's the max usage.

@mnocon
Copy link
Contributor Author

mnocon commented Jun 11, 2025

@konradoboza thank you for your suggestions, I've applied them in e141ad2

Please note that I've added a small note on Discount Statuses.

@mnocon mnocon requested a review from konradoboza June 11, 2025 08:15
@mnocon mnocon requested a review from a team June 11, 2025 08:26
@ezrobot ezrobot requested review from adriendupuis, dabrt and julitafalcondusza and removed request for a team June 11, 2025 08:26
Copy link
Contributor

@adriendupuis adriendupuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm lost with expression values and condition Identifiers in REST. Maybe it's only for migration, not REST?

Co-authored-by: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com>
Copy link

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/discounts/src/Command/ManageDiscountsCommand.php


code_samples/discounts/src/Command/ManageDiscountsCommand.php

docs/discounts/discounts_api.md@118:``` php hl_lines="60-66 68-92"
docs/discounts/discounts_api.md@119:[[= include_file('code_samples/discounts/src/Command/ManageDiscountsCommand.php') =]]
docs/discounts/discounts_api.md@120:```

001⫶<?php
002⫶
003⫶declare(strict_types=1);
004⫶
005⫶namespace App\Command;
006⫶
007⫶use DateTimeImmutable;
008⫶use Ibexa\Contracts\Core\Collection\ArrayMap;
009⫶use Ibexa\Contracts\Core\Repository\PermissionResolver;
010⫶use Ibexa\Contracts\Core\Repository\UserService;
011⫶use Ibexa\Contracts\Discounts\DiscountServiceInterface;
012⫶use Ibexa\Contracts\Discounts\Value\DiscountType;
013⫶use Ibexa\Contracts\Discounts\Value\Struct\DiscountCreateStruct;
014⫶use Ibexa\Contracts\Discounts\Value\Struct\DiscountTranslationStruct;
015⫶use Ibexa\Contracts\DiscountsCodes\DiscountCodeServiceInterface;
016⫶use Ibexa\Contracts\DiscountsCodes\Value\Struct\DiscountCodeCreateStruct;
017⫶use Ibexa\Discounts\Value\DiscountCondition\IsInCurrency;
018⫶use Ibexa\Discounts\Value\DiscountCondition\IsInRegions;
019⫶use Ibexa\Discounts\Value\DiscountCondition\IsProductInArray;
020⫶use Ibexa\Discounts\Value\DiscountRule\FixedAmount;
021⫶use Ibexa\DiscountsCodes\Value\DiscountCondition\IsValidDiscountCode;
022⫶use Symfony\Component\Console\Command\Command;
023⫶use Symfony\Component\Console\Input\InputInterface;
024⫶use Symfony\Component\Console\Output\OutputInterface;
025⫶
026⫶final class ManageDiscountsCommand extends Command
027⫶{
028⫶ protected static $defaultName = 'discounts:manage';
029⫶
030⫶ private DiscountServiceInterface $discountService;
031⫶
032⫶ private DiscountCodeServiceInterface $discountCodeService;
033⫶
034⫶ private PermissionResolver $permissionResolver;
035⫶
036⫶ private UserService $userService;
037⫶
038⫶ public function __construct(
039⫶ UserService $userSerice,
040⫶ PermissionResolver $permissionResolver,
041⫶ DiscountServiceInterface $discountService,
042⫶ DiscountCodeServiceInterface $discountCodeService
043⫶ ) {
044⫶ $this->userService = $userSerice;
045⫶ $this->discountService = $discountService;
046⫶ $this->discountCodeService = $discountCodeService;
047⫶ $this->permissionResolver = $permissionResolver;
048⫶
049⫶ parent::__construct();
050⫶ }
051⫶
052⫶ protected function execute(InputInterface $input, OutputInterface $output): int
053⫶ {
054⫶ $this->permissionResolver->setCurrentUserReference(
055⫶ $this->userService->loadUserByLogin('admin')
056⫶ );
057⫶
058⫶ $now = new DateTimeImmutable();
059⫶
060❇️ $discountCodeCreateStruct = new DiscountCodeCreateStruct(
061❇️ 'summer10',
062❇️ null, // Unlimited usage
063❇️ $this->permissionResolver->getCurrentUserReference()->getUserId(),
064❇️ $now
065❇️ );
066❇️ $discountCode = $this->discountCodeService->createDiscountCode($discountCodeCreateStruct);
067⫶
068❇️ $discountCreateStruct = new DiscountCreateStruct();
069❇️ $discountCreateStruct
070❇️ ->setIdentifier('discount_identifier')
071❇️ ->setType(DiscountType::CART)
072❇️ ->setPriority(10)
073❇️ ->setEnabled(true)
074❇️ ->setUser($this->userService->loadUserByLogin('admin'))
075❇️ ->setRule(new FixedAmount(10))
076❇️ ->setStartDate($now)
077❇️ ->setConditions([
078❇️ new IsInRegions(['germany', 'france']),
079❇️ new IsProductInArray(['product-1', 'product-2']),
080❇️ new IsInCurrency('EUR'),
081❇️ new IsValidDiscountCode($discountCode->getCode(), $discountCode->getUsedLimit()),
082❇️ ])
083❇️ ->setTranslations([
084❇️ new DiscountTranslationStruct('eng-GB', 'Discount name', 'This is a discount description', 'Promotion Label', 'Promotion Description'),
085❇️ new DiscountTranslationStruct('ger-DE', 'Discount name (German)', 'Description (German)', 'Promotion Label (German)', 'Promotion Description (German)'),
086❇️ ])
087❇️ ->setEndDate(null) // Permanent discount
088❇️ ->setCreatedAt($now)
089❇️ ->setUpdatedAt($now)
090❇️ ->setContext(new ArrayMap(['custom_context' => 'custom_value']));
091❇️
092❇️ $this->discountService->createDiscount($discountCreateStruct);
093⫶
094⫶ return Command::SUCCESS;
095⫶ }
096⫶}

Download colorized diff

@adriendupuis adriendupuis merged commit ca47e18 into discounts-rest Jun 16, 2025
7 checks passed
@adriendupuis adriendupuis deleted the discounts-api branch June 16, 2025 07:42
adriendupuis added a commit that referenced this pull request Jun 16, 2025
* [Discounts] Described Discounts API (#2783)
* [Discounts] Configuration (#2781)

---------

Co-authored-by: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com>
adriendupuis added a commit that referenced this pull request Jun 16, 2025
* [Discounts] Described Discounts API (#2783)
* [Discounts] Configuration (#2781)

---------

Co-authored-by: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants